﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.IdentityServer.ClaimsPolicy.Engine.AttributeStore;
using System.IdentityModel;
using System.Net;
using System.IO;
using Newtonsoft.Json;

namespace CNRS.coManageAS
{
    public class CNRScoManageAS : IAttributeStore
    {
        // configurations variables
        string _wsUrl;          // coManage web service URL
        string _coOrgID;           // the id of the comamane organozation
        string _userName;       // the access login
        string _password;       // the access login password
        string _authorization;  // basic web authorization token

        /// <summary>
        /// the called method from ADFS on each binds
        /// </summary>
        /// <param name="query">unused, as the query is unique and spécific to CoManage web service</param>
        /// <param name="parameters"></param>
        /// <param name="callback"></param>
        /// <param name="state"></param>
        /// <returns></returns>
        public IAsyncResult BeginExecuteQuery(string query, string[] parameters, AsyncCallback callback, object state)
        {
            // attributes in the query are separated with ,
            Char[] attributeSplitChar = new Char[] { ',' };

            if (parameters == null)
            {
                throw new AttributeStoreQueryFormatException("No query parameter.");
            }

            if (parameters.Length != 1)
            {
                throw new AttributeStoreQueryFormatException("More than one query parameter.");
            }

            // Get the user identifier (email, UPN, EPPN) through the unique parameter
            string inputString = parameters[0];

            if (inputString == null)
            {
                throw new AttributeStoreQueryFormatException("Query parameter cannot be null.");
            }

            // Call Grouper with Voot protocol
            string coIdentifier = GetDataElements(inputString);

            // Transform the voot call result to attribute store values
            string[][] outputValues = new string[1][];

            outputValues[0] = new string[] { coIdentifier };

            // Return the output array
            TypedAsyncResult<string[][]> asyncResult = new TypedAsyncResult<string[][]>(callback, state);
            asyncResult.Complete(outputValues, true);
            return asyncResult;
        }

        public string[][] EndExecuteQuery(IAsyncResult result)
        {
            return TypedAsyncResult<string[][]>.End(result);
        }

        public void Initialize(Dictionary<string, string> config)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            if (!config.TryGetValue("serviceurl", out _wsUrl))
            {
                throw new AttributeStoreInvalidConfigurationException("serviceurl configuration entry not found");
            }

            if (string.IsNullOrEmpty(_wsUrl))
            {
                throw new AttributeStoreInvalidConfigurationException("serviceurl cannot be empty");
            }


            if (!config.TryGetValue("username", out _userName))
            {
                throw new AttributeStoreInvalidConfigurationException("username configuration entry not found");
            }

            if (string.IsNullOrEmpty(_userName))
            {
                throw new AttributeStoreInvalidConfigurationException("username cannot be empty");
            }

            if (!config.TryGetValue("password", out _password))
            {
                throw new AttributeStoreInvalidConfigurationException("password configuration entry not found");
            }

            if (string.IsNullOrEmpty(_password))
            {
                throw new AttributeStoreInvalidConfigurationException("password cannot be empty");
            }

            if (!config.TryGetValue("orgid", out _coOrgID))
            {
                throw new AttributeStoreInvalidConfigurationException("orgid configuration entry not found");
            }

            if (string.IsNullOrEmpty(_coOrgID))
            {
                throw new AttributeStoreInvalidConfigurationException("password cannot be empty");
            }

            // build the basic web authorization token 
            string authorization = _userName + ":" + _password;
            byte[] binaryAuthorization = System.Text.Encoding.UTF8.GetBytes(authorization);
            authorization = Convert.ToBase64String(binaryAuthorization);
            _authorization = "Basic " + authorization;

        }

        #region Data
        public string GetDataElements(string userID)
        {
            //LogHelper.LogDebug(LogCategorie.VootHelper, string.Format("Searching '{0}' for 'element' -> '{1}'", searchPattern, element.Key));

            string json = string.Empty;
            string wsQuery;

            string coIdentifier = string.Empty;
            string coOrgIdentityId = string.Empty;
            string coPersonId = string.Empty;
            bool coPersonIdMatches = false;


            // we are suppose to always have a value, so just in case
            if (string.IsNullOrEmpty(userID))
            {
                return coIdentifier;
            }

            // goto all the steps to infer the coManage ID for the user for this VO

            // step 1 : get 
            wsQuery = string.Format("org_identities.json?coid={0}&search_identifier={1}", _coOrgID, userID);
            json = CallWebService(wsQuery);
            if (!string.IsNullOrEmpty(json))
            {
                // transform the json string to .Net object
                var res = JsonConvert.DeserializeObject<OrgIdentityRoot>(json);
                coOrgIdentityId = res.OrgIdentities[0].Id;
            }

            // step 2
            if (!string.IsNullOrEmpty(coOrgIdentityId))
            {
                wsQuery = string.Format("co_org_identity_links.json?orgidentityid={0}", coOrgIdentityId);
                json = CallWebService(wsQuery);
                if (!string.IsNullOrEmpty(json))
                {
                    // transform the json string to .Net object
                    var res = JsonConvert.DeserializeObject<CoOrgIdentityLinkRoot>(json);
                    coPersonId = res.CoOrgIdentityLinks[0].CoPersonId;
                }
            }
            // step 3
            if (!string.IsNullOrEmpty(coPersonId))
            {
                wsQuery = string.Format("co_people/{0}.json", coPersonId);
                json = CallWebService(wsQuery);
                if (!string.IsNullOrEmpty(json))
                {
                    // transform the json string to .Net object
                    var res = JsonConvert.DeserializeObject<CoPeopleRoot>(json);
                    foreach (CoPeople coPeople in res.CoPeople)
                    {
                        if (coPeople.CoId == _coOrgID)
                        {
                            coPersonIdMatches = true;
                            break;
                        }
                    }
                }
            }

            // step 4
            if (coPersonIdMatches)
            {
                wsQuery = string.Format("identifiers.json?copersonid={0}", coPersonId);
                json = CallWebService(wsQuery);
                if (!string.IsNullOrEmpty(json))
                {
                    // transform the json string to .Net object
                    var res = JsonConvert.DeserializeObject<IdentifierRoot>(json);
                    coIdentifier = res.Identifiers[0].Identifier;
                }
            }
            //LogHelper.LogDebug(LogCategorie.VootHelper, string.Format("Elements returned -> '{0}'", dataElements.Count));
            return coIdentifier;
        }
        #endregion

        #region WebCall
        public string CallWebService(string searchPattern)
        {
            //LogHelper.LogDebug(LogCategorie.VootHelper, string.Format("Searching '{0}' for 'element' -> '{1}'", searchPattern, element.Key));

            //string typeKey = element.Type + element.Key;
            //string nodeID = element.Key;
            //string nodeLabel = element.Title;

            string json = string.Empty;

            if (!string.IsNullOrEmpty(searchPattern))
            {

                string searchUrl = _wsUrl + "/registry/" + searchPattern;
                // TODO : work out search in Grouper

                try
                {
                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(searchUrl);
                    request.Method = "GET";
                    //request.ContentType = "application/json";
                    request.Headers.Add("AUTHORIZATION", _authorization);

                    // call grouper voot web service
                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    if (response.StatusCode != HttpStatusCode.OK)
                    {
                        //LogHelper.LogError(("http status code : " + response.StatusCode), response.Headers.ToString());
                    }
                    else
                    {
                        // store the response in a string
                        using (Stream responseStream = response.GetResponseStream())
                        {
                            StreamReader stream = new StreamReader(responseStream, Encoding.UTF8);
                            json = stream.ReadToEnd();
                            stream.Close();
                        }
                    }
                }
                catch (WebException ex)
                {
                    WebResponse errorResponse = ex.Response;
                    using (Stream responseStream = errorResponse.GetResponseStream())
                    {
                        StreamReader reader = new StreamReader(responseStream, Encoding.GetEncoding("utf-8"));
                        String errorText = reader.ReadToEnd();
                        //LogHelper.LogError(ex, errorText);

                    }
                }
            }

            //LogHelper.LogDebug(LogCategorie.VootHelper, string.Format("Elements returned -> '{0}'", dataElements.Count));
            return json;
        }

        #endregion
    }
}
